package bsh;

import java.util.List;
import java.util.Map;

import org.objectweb.asm.MethodVisitor;

import bsh.asm.acessor.ObjectAccessor;
import bsh.compilation.CompilationOptions;
import bsh.exception.CompilationFailedException;
import bsh.meta.FieldDescriptor;
import bsh.meta.MethodDescriptor;
import bsh.meta.Scope;
import bsh.meta.ScopeImpl;
import bsh.meta.TypeWrapper;
import bsh.meta.UnfinishedInnerType;
import bsh.meta.UnfinishedType;

/**
 * A compilation unit holds a reference to the class being compiled
 * 
 * @author takeshi
 */
public interface CompilationUnit {

    /**
     * Creates a new {@link CompilationUnit} with an inner type
     * 
     * @param clName the class name
     * @param superType the superType
     * @param ifs the declaring interfaces
     * @param declaringScope the declaring scope
     * @param modifiers the modifiers
     * @param simpleName the simpleName
     * @return {@link CompilationUnit}
     */
    CompilationUnit createInnerType(String clName, TypeWrapper superType, List<? extends TypeWrapper> ifs, Scope declaringScope,
            int modifiers, String simpleName);

    /**
     * Return a {@link FieldDescriptor} for a field name or <code>null</code> of none is found
     * 
     * @param name String
     * @return {@link FieldDescriptor}
     */
    FieldDescriptor getField(String name);

    /**
     * @return String
     */
    String getThisClassDescriptor();

    /**
     * @return {@link CompilerManagerInternals}
     */
    CompilerManagerInternals getCompilerManager();

    /**
     * @return {@link CompilationOptions}
     */
    CompilationOptions options();

    /**
     * Procura ou instancia um compiler para o no especificado
     * 
     * @param node {@link SimpleNode}
     * @return {@link NodeCompiler}
     * @throws CompilationFailedException e
     */
    NodeCompiler getNodeCompiler(SimpleNode node) throws CompilationFailedException;

    //
    // /**
    // * Resolve um tipo, contando com os imports
    // *
    // * @param name String
    // * @return {@link TypeWrapper}
    // */
    // TypeWrapper resolveType(String name);

    /**
     * Adiciona um import, nao necessariamente para o nome da classe
     * 
     * @param name String
     * @param type {@link TypeWrapper}
     */
    // void addTypeAlias(String name, TypeWrapper type);

    /**
     * Adiciona um import .*
     * 
     * @param packageName String
     */
    // void addWildcardImport(String packageName);

    /**
     * Devolve o tipo que o no deixara no stack
     * 
     * @param node {@link SimpleNode}
     * @param scope {@link Scope}
     * @return {@link TypeWrapper}
     * @throws CompilationFailedException e
     */
    TypeWrapper getNodeType(SimpleNode node, Scope scope) throws CompilationFailedException;

    /**
     * @return {@link ReflectionUtils}
     */
    ReflectionUtils reflectionUtils();

    /**
     * Devolve o tipo sendo construido
     * 
     * @return {@link UnfinishedType}
     */
    UnfinishedType thisType();

    /**
     * Adiciona um campo
     * 
     * @param name String
     * @param type {@link TypeWrapper}
     * @param modifiers the modifiers
     * @return {@link FieldDescriptor}
     */
    FieldDescriptor addField(String name, TypeWrapper type, int modifiers);

    /**
     * adiciona um campo estatico
     * 
     * @param name o nome
     * @param type o tipo
     * @param options TODO
     * @return {@link FieldDescriptor}
     */
    FieldDescriptor addStaticField(String name, TypeWrapper type, int options);

    /**
     * Adiciona um metodo
     * 
     * @param methodDescriptor {@link MethodDescriptor}
     */
    void addMethod(MethodDescriptor methodDescriptor);

    /**
     * @param string String
     * @return {@link ObjectAccessor}
     */
    ObjectAccessor getFieldAcessor(String string);

    /**
     * Devolve o escopo global
     * 
     * @return {@link ScopeImpl}
     */
    ScopeImpl compilationUnitScope();

    /**
     * Atalho para compilar um noh
     * 
     * @param child No
     * @param visitor {@link MethodVisitor}
     * @param state {@link CompilationState}
     * @throws CompilationFailedException e
     */
    void compileNode(SimpleNode child, MethodVisitor visitor, CompilationState state) throws CompilationFailedException;

    /**
     * Adds an inner class definition
     * 
     * @param data the bytecode
     * @param innerType the {@link UnfinishedInnerType}
     */
    void addInnerClassDefinition(byte[] data, UnfinishedInnerType innerType);

    /**
     * @return the global scope of this {@link CompilationUnit}
     */
    Scope getGlobalScope();

    /**
     * @return {@link Map} of class name and bytecode
     */
    Map<String, byte[]> getDependentTypes();

}
